
#include "conn_writer.h"

#include <unistd.h>
#include <string.h>
#include <sys/socket.h>

#include <iostream>

#include "sunnet.h"


void ConnWriter::EntireWriteWhenEmpty(std::shared_ptr<char> buff, std::streamsize len) {
    char* s = buff.get() ;
    //谨记：>=0, -1&&EAGAIN, -1&&EINTR, -1&&其他
    std::streamsize n = ::write(fd_, s, len);
    if(n < 0 && errno == EINTR) { }; //仅提醒你要注意

    LOG_DEBUG("EntireWriteWhenEmpty write n={}", n);
    //情况1-1：全部写完
    if(n >= 0 && n == len) {
        return;
    }
    //情况1-2：写一部分（或没写入）
    if( (n > 0 && n < len) || (n < 0 && errno == EAGAIN) ) {
        if(n < 0){
            n = 0;
        }
        auto obj = std::make_shared<WriteObject>();
        obj->start_ = n;
        obj->buff_ = buff;
        obj->len_ = len;
        objs_.push_back(obj);
        Sunnet::GetInstance().ModifyEvent(fd_, true);
        return;
    }
    //情况1-3：真的发生错误
    LOG_ERR("EntireWrite write error ");
    is_error_ = true;
    HandleError();
}

void ConnWriter::EntireWriteWhenNotEmpty(std::shared_ptr<char> buff, std::streamsize len) {
    auto obj = std::make_shared<WriteObject>();
    obj->start_ = 0;
    obj->buff_ = buff;
    obj->len_ = len;
    objs_.push_back(obj);
}

void ConnWriter::EntireWrite(std::shared_ptr<char> buff, std::streamsize len) {
    if(is_closing_){
        LOG_INFO("EntireWrite fail, because isClosing");
        return;
    }
    //情况1：没有待写入数据，先尝试写入
    if(objs_.empty()) {
        EntireWriteWhenEmpty(buff, len);
    }
    //情况2：有待写入数据，添加到末尾
    else{
        EntireWriteWhenNotEmpty(buff, len);
    }
}


//返回值:是否完整的写入了一条
bool ConnWriter::WriteFrontObj() {
    //没待写数据
    if(objs_.empty()) {
        return false;
    }
    //获取第一条
    auto obj = objs_.front();

    //谨记：>=0, -1&&EAGAIN, -1&&EINTR, -1&&其他
    char* s = obj->buff_.get() + obj->start_;
    int len = obj->len_ - obj->start_;
    int n = ::write(fd_, s, len);

    LOG_DEBUG("WriteFrontObj write n={}", n);
    if(n < 0 && errno == EINTR) { }; //仅提醒你要注意
    //情况1-1：全部写完
    if(n >= 0 && n == len) {
        objs_.pop_front(); //出队
        return true;
    }
    //情况1-2：写一部分（或没写入）
    if( (n > 0 && n < len) || (n < 0 && errno == EAGAIN) ) {
        if(n < 0){
            n = 0;
        }
        obj->start_ += n;
        return false;
    }
    //情况1-3：真的发生错误
    LOG_ERR("EntireWrite write error ");
    
    is_error_ = true;
    return false;
}

void ConnWriter::OnWriteable() {
    auto conn = Sunnet::GetInstance().GetConn(fd_);
    if(conn == NULL){ //连接已关闭
        return;
    }

    while(WriteFrontObj()){
        //循环
    }

    if(HandleError()){
        return;
    }
    
    if(objs_.empty()) {
        Sunnet::GetInstance().ModifyEvent(fd_, false);

        if(is_closing_) {
            //通知服务，此处并不是通用做法
            //让read产生 Bad file descriptor报错
            LOG_INFO("linger close conn");

            ::shutdown(fd_, SHUT_RD);
            auto msg= std::make_shared<SocketRWMsg>();
            msg->SetType(BaseMsg::MsgType::SOCKET_RW);
            msg->SetFd(conn->fd_);
            msg->SetClose(true);
            Sunnet::GetInstance().Send(conn->GetServiceID(), msg);
        }
    }
}

void ConnWriter::LingerClose(){
    if(is_closing_){
        return;
    }
    is_closing_ = true;
    if(objs_.empty()) {
        Sunnet::GetInstance().CloseConn(fd_);
        return;
    }
}

bool ConnWriter::HandleError(){
    if(is_error_){
        auto conn = Sunnet::GetInstance().GetConn(fd_);
        if(conn == NULL){ //连接已关闭
            return true;
        }
        is_closing_ = true;
        //通知服务，此处并不是通用做法
        //让read产生 Bad file descriptor报错
        LOG_INFO("HandleError close");

        ::shutdown(fd_, SHUT_RD);
        auto msg= std::make_shared<SocketRWMsg>();
        msg->SetType(BaseMsg::MsgType::SOCKET_RW);
        msg->SetFd(conn->fd_);
        msg->SetClose(true);
        Sunnet::GetInstance().Send(conn->GetServiceID(), msg);
        return true;
    }

    return false;
}
